package com.lanchetech.user.service.impl;


import com.alibaba.excel.util.CollectionUtils;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.lanchetech.bean.request.PageReq;
import com.lanchetech.bean.response.BasePageResp;
import com.lanchetech.bean.response.CoinOrderResp;
import com.lanchetech.bean.response.CoinProductResp;
import com.lanchetech.bean.response.ResultData;
import com.lanchetech.bean.util.PageHelpUtil;
import com.lanchetech.bean.vo.CoinProductVO;
import com.lanchetech.common.enums.*;
import com.lanchetech.common.exception.ResultCodeException;
import com.lanchetech.dao.*;
import com.lanchetech.entity.*;
import com.lanchetech.service.DaoService;
import com.lanchetech.user.service.CoinService;
import com.lanchetech.user.service.UserService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.stream.Collectors;

@Service
public class CoinServiceImpl implements CoinService {

    @Autowired
    CoinProductMapper coinProductMapper;

    @Autowired
    DaoService daoService;

    @Autowired
    SpuMapper spuMapper;

    @Autowired
    UserService userService;

    @Autowired
    CoinOrderMapper coinOrderMapper;

    @Autowired
    UserAddressMapper userAddressMapper;

    @Autowired
    CoinOrderAddressMapper coinOrderAddressMapper;

    @Autowired
    UserMapper userMapper;

    @Autowired
    CoinFlowMapper coinFlowMapper;

    @Override
    public ResultData<BasePageResp<CoinProductResp>> getCoinProductPage(PageReq req) {
        req.setStatus(ToggleEnum.ON.getStatus());
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        List<CoinProductResp> list = coinProductMapper.findAllPage(req);
        if (!CollectionUtils.isEmpty(list)) {
            Map<Long, Spu> map = daoService.getSpuMap(list.stream().map(CoinProduct::getSpuId).collect(Collectors.toList()));
            list.stream().forEach(item -> {
                item.setMainImage(map.get(item.getSpuId()).getMainImage());
                item.setProductName(map.get(item.getSpuId()).getName());
                item.setPrice(map.get(item.getSpuId()).getMinPrice());
                item.setIsVirtual(map.get(item.getSpuId()).getIsVirtual());
            });
        }
        return PageHelpUtil.buildPage(list, page);
    }

    @Override
    public ResultData<CoinProductVO> getCoinProductDetail(User user, Long id) {
        CoinProductVO vo = new CoinProductVO();
        CoinProduct coinProduct = coinProductMapper.selectByPrimaryKey(id);
        Spu spu = spuMapper.selectByPrimaryKey(coinProduct.getSpuId());
        vo.setCoinProduct(coinProduct);
        vo.setSpu(spu);
        if (user != null) {
            vo.setUserAddress(userService.getMainUserAddress(user).getValue());
            vo.setTotalCount(coinOrderMapper.sumByUserIdAndCoinProductId(user.getId(), id));
        }
        return new ResultData<>(vo);
    }

    /**
     * 计算抽奖结果
     *
     * @param probability
     * @return
     */
    private static Byte computedProbability(BigDecimal probability) {
        if(probability.compareTo(new BigDecimal(100)) >= 0){
            return (byte) 1;
        }

        // 0.01先乘以100，等于1，然后在10000中，给1加上0~10000-1的随机数x，那么 (x, x + 1]就是中奖区间，然后生成0至10000的随机数，如果大于x小于x+1则中奖
        // probability 只能是0.01至100的数字
        // 先变整数
        BigDecimal probabilityNum = probability.multiply(new BigDecimal(100));
        // 获取一个 0 到 10000 - probabilityNum 的随机数
        Random random = new Random();
        BigDecimal startNum = new BigDecimal(random.nextInt(new BigDecimal(10000).subtract(probabilityNum).intValue()));
        BigDecimal endNum = startNum.add(probabilityNum);

        // 该用户的中奖数
        BigDecimal lotteryNum = new BigDecimal(random.nextInt(10000));
        // 如果中奖数在概率区间内，则中奖
        if (lotteryNum.compareTo(startNum) > 0 && lotteryNum.compareTo(endNum) <= 0) {
            return (byte) 1;
        } else {
            return (byte) 0;
        }
    }

    private void createCoinOrderAddress(Long addressId, Long coinOrderId) {
        if (addressId != null) {
            UserAddress userAddress = userAddressMapper.selectByPrimaryKey(addressId);
            CoinOrderAddress coinOrderAddress = CoinOrderAddress.builder().build();
            BeanUtils.copyProperties(userAddress, coinOrderAddress);
            coinOrderAddress.setId(null);
            coinOrderAddress.setCoinOrderId(coinOrderId);
            coinOrderAddress.setUserAddressId(userAddress.getId());
            coinOrderAddressMapper.insert(coinOrderAddress);
        }
    }

    @Override
    public ResultData<Boolean> createCoinOrder(User user, CoinOrder req) {
        ResultData<Boolean> resp = new ResultData<>();
        try {
            Long totalCount = coinOrderMapper.sumByUserIdAndCoinProductId(user.getId(), req.getCoinProductId());
            if (totalCount == null) {
                totalCount = 0L;
            }
            CoinProduct coinProduct = coinProductMapper.selectByPrimaryKey(req.getCoinProductId());
            BigDecimal totalCoin = coinProduct.getCostCoin().multiply(new BigDecimal(req.getCount()));

            if (coinProduct.getLimitCount() > 0 && coinProduct.getLimitCount() < (totalCount + req.getCount())) {
                if (CoinProductTypeEnum.EXCHANGE.getType().equals(coinProduct.getType())) {
                    throw new ResultCodeException(ResultCode.COIN_PRODUCT_EXCHANGE_OVER_MAX);
                } else {
                    throw new ResultCodeException(ResultCode.COIN_PRODUCT_LOTTERY_OVER_MAX);
                }
            }
            if (coinProduct.getStartTime().getTime() > System.currentTimeMillis()) {
                throw new ResultCodeException(ResultCode.ACTIVITY_NOT_START);
            }
            if (coinProduct.getEndTime().getTime() < System.currentTimeMillis()) {
                throw new ResultCodeException(ResultCode.ACTIVITY_HAS_END);
            }
            if (ToggleEnum.OFF.getStatus().equals(coinProduct.getStatus()) || DeletedEnum.DELETED.getStatus().equals(coinProduct.getDeleted())) {
                throw new ResultCodeException(ResultCode.ACTIVITY_HAS_END);
            }
            if (user.getCoin().compareTo(totalCoin) < 0) {
                throw new ResultCodeException(ResultCode.COIN_NOT_ENOUGH);
            }
            if (coinProduct.getStock() < req.getCount()) {
                throw new ResultCodeException(ResultCode.COIN_PRODUCT_STOCK_NOT_ENOUGH);
            }

            // 参于活动
            Byte isGetLottery = (byte) 0;
            if (CoinProductTypeEnum.LOTTERY.getType().equals(coinProduct.getType())) {
                isGetLottery = computedProbability(coinProduct.getProbability());
            }

            CoinOrder coinOrder = CoinOrder.builder()
                    .coinProductId(req.getCoinProductId())
                    .userId(user.getId())
                    .spuId(coinProduct.getSpuId())
                    .count(req.getCount())
                    .totalCostCoin(totalCoin)
                    .logisticsStatus(LogisticsStatusEnum.UNDO.getStatus())
                    .type(coinProduct.getType())
                    .userAddressId(req.getUserAddressId())
                    .lotteryStatus(isGetLottery)
                    .createdAt(new Date())
                    .updatedAt(new Date())
                    .build();
            coinOrderMapper.insert(coinOrder);

            if (CoinProductTypeEnum.EXCHANGE.getType().equals(coinProduct.getType())) {
                coinProduct.setStock(coinProduct.getStock() - req.getCount());
                coinProduct.setSales(coinProduct.getSales() + req.getCount());
                createCoinOrderAddress(req.getUserAddressId(), coinOrder.getId());
            } else {
                if (isGetLottery.equals((byte) 1)) {
                    coinProduct.setStock(coinProduct.getStock() - req.getCount());
                    createCoinOrderAddress(req.getUserAddressId(), coinOrder.getId());
                }
                coinProduct.setSales(coinProduct.getSales() + req.getCount());
            }

            // 增加参与记录
            coinProductMapper.updateByPrimaryKeySelective(coinProduct);
            // 扣除用户的积分余额
            user.setCoin(user.getCoin().subtract(totalCoin));
            userMapper.updateCoinById(user.getCoin(), user.getId());

            // 创建积分使用记录
            coinFlowMapper.insert(CoinFlow.builder()
                    .userId(user.getId())
                    .amount(totalCoin)
                    .tradeType(coinProduct.getType().equals(CoinProductTypeEnum.EXCHANGE.getType()) ? CoinFlowTradeTypeEnum.EXCHANGE.getTradeType() : CoinFlowTradeTypeEnum.LOTTERY.getTradeType())
                    .type((byte) 1)
                    .coin(user.getCoin())
                    .relateId(coinOrder.getId())
                    .remark(coinProduct.getProductName())
                    .createdAt(new Date())
                    .build());

            resp.setValue(isGetLottery.equals((byte) 1));
            return resp;
        } catch (ResultCodeException e) {
            resp.setResultCode(e.getResultCode());
            return resp;
        }

    }

    @Override
    public ResultData<BasePageResp<CoinOrderResp>> getCoinOrderPage(User user, PageReq req) {
        req.setUserId(user.getId());
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        List<CoinOrderResp> list = coinOrderMapper.findAllFrontPage(req);

        if (!CollectionUtils.isEmpty(list)) {
            Map<Long, Spu> spuMap = daoService.getSpuMap(list.stream().map(CoinOrder::getSpuId).collect(Collectors.toList()));
            Map<String, LogisticsCompany> companyMap = daoService.getLogisticsCompanyMap();
            list.stream().forEach(item -> {
                item.setMainImage(spuMap.get(item.getSpuId()).getMainImage());
                item.setProductName(spuMap.get(item.getSpuId()).getName());
                item.setPrice(spuMap.get(item.getSpuId()).getMinPrice());
                item.setLogisticsCompanyName(item.getLogisticsCompany() == null ? null : companyMap.get(item.getLogisticsCompany()).getName());
            });
        }

        return PageHelpUtil.buildPage(list, page);
    }

    @Override
    public ResultData<CoinOrderResp> getCoinOrderDetail(Long id) {
        CoinOrderResp resp = new CoinOrderResp();
        CoinOrder coinOrder = coinOrderMapper.selectByPrimaryKey(id);
        User currentUser = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if(!coinOrder.getUserId().equals(currentUser.getId())){
            return new ResultData<>(null);
        }
        BeanUtils.copyProperties(coinOrder, resp);
        Spu spu = spuMapper.selectByPrimaryKey(coinOrder.getSpuId());

        resp.setMainImage(spu.getMainImage());
        resp.setProductName(spu.getName());
        resp.setPrice(spu.getMinPrice());
        resp.setIsVirtual(spu.getIsVirtual());

        if(coinOrder.getUserAddressId() != null){
            resp.setCoinOrderAddress(coinOrderAddressMapper.findOneByCoinOrderId(id));
        }else{
            User user = userMapper.selectByPrimaryKey(coinOrder.getUserId());
            resp.setNickname(user.getNickname());
            resp.setPhone(user.getUsername());
        }
        return new ResultData<>(resp);
    }
}